package org.pixelami.twisted.spread.jelly
{
	import org.pixelami.twisted.persisted.crefutil.NotKnown;
	import org.pixelami.twisted.utils.PyTuple;
	import org.pixelami.twisted.utils.__builtin__;
	
	public class _Unjellier
	{
		public var taster:Object;
		public var persistentLoad:Object;
		public var references:Object;
		public var postCallbacks:Array;
		public var invoker:Object;
		
		
		public function _Unjellier(taster:Object, persistentLoad:Object, invoker:Object)
		{
			this.taster = taster
	        this.persistentLoad = persistentLoad
	        this.references = {}
	        this.postCallbacks = []
	        this.invoker = invoker
		}
		
		public function unjellyFull(obj:Object):Object
		{
			var o:Object = this.unjelly(obj)
	        for each(var m:Function in this.postCallbacks)
	        {
	            m()
	        }
	        return o;
		}

	        
	
	    public function unjelly(obj:Object):Object
	    {
	        // obj will be a nested array ?
	        
	        if (!(obj is Array))
	            return obj
	        
	        var jelType:String = obj[0];
	        //trace("jelType = "+jelType);
	        
	        
	        /*
	        
	        if type(obj) is not types.ListType:
	            return obj
	        jelType = obj[0]
	        */
	        
	        
	        
	        if(!(this.taster.isTypeAllowed(jelType)))
	        {
	            throw new Error("InsecureJelly(jelType)");
	        }
	        
	        var inst:*
	        var regClass:* = Jelly.unjellyableRegistry[jelType];
	        
	        if((regClass != null))
	        {
	            
	            if (regClass is Class)
	            {
	                inst = new regClass//_Dummy() //# XXX chomp, chomp
	                //inst.__class__ = regClass
	                //_Dummy.prototype = regClass;
	                method = inst.unjellyFor
	            }
	            /*
	            else if(regClass is type)
	            {
	                //# regClass.__new__ does not call regClass.__init__
	                var inst = regClass.__new__(regClass)
	                method = inst.unjellyFor
	            }
	            */
	            else
	            {
	            
	                var method:* = regClass //# this is how it ought to be done
	            
	            }
	            
	            var val:Object = method(this, obj)
	            if (val.hasOwnProperty('postUnjelly'))
	            {
	                this.postCallbacks.push(inst.postUnjelly)
	            }
	            return val;
	        }
	        var regFactory:* = Jelly.unjellyableFactoryRegistry[jelType];
	        
	        if (regFactory != null)
	        {
	            var state:* = this.unjelly(obj[1]);
	            inst = regFactory(state);
	            if (inst.hasOwnProperty('postUnjelly'))
	            {
	                this.postCallbacks.append(inst.postUnjelly);
	            }
	            return inst;
	        }
	        /*
	        thunk = getattr(this, '_unjelly_%s'%jelType, None)
	        if thunk is not None:
	            ret = thunk(obj[1:])
	        
	        
	        */
	        var ret:*;
	        var fn:Function;
	        try 
	        {
	        	//trace('_unjelly_'+jelType);
	        	fn = this['_unjelly_'+jelType];
	        	////trace('_unjelly_'+jelType);
	        } 
	        catch(e:Error)
	        {
	        	fn = null;
	        	ret = obj.slice();
	        }
	        if (fn != null)
	        {
	            var sexp:Array = obj.splice(1);
	            //trace("sexp args = "+sexp);
	            ret = fn(sexp);
	        }
	        /*
	        else:
	            nameSplit = string.split(jelType, '.')
	            modName = string.join(nameSplit[:-1], '.')
	            if not this.taster.isModuleAllowed(modName):
	                raise InsecureJelly("Module %s not allowed (in type %s)." % (modName, jelType))
	            clz = namedObject(jelType)
	            if not this.taster.isClassAllowed(clz):
	                raise InsecureJelly("Class %s not allowed." % jelType)
	            if hasattr(clz, "__setstate__"):
	                ret = _newInstance(clz, {})
	                state = this.unjelly(obj[1])
	                ret.__setstate__(state)
	            else:
	                state = this.unjelly(obj[1])
	                ret = _newInstance(clz, state)
	            if hasattr(clz, 'postUnjelly'):
	                this.postCallbacks.append(ret.postUnjelly)
			return ret
			*/
	        
	        return ret;
	    }
	    
	    
	    
	    private function _unjelly_boolean(exp:String):Boolean
	    {
	        //if (Boolean)
	        {
	            //assert exp[0] in ('true', 'false');
	            if(!(exp[0] == 'true' || exp[0] == 'false')) throw new Error("Assertion Error");
	            return exp[0] == 'true';
	        }
	        //else
	        {
	            //return Unpersistable(exp[0]);
	        }
	    }
	    
	    private function _unjelly_tuple(lst:Array):PyTuple
	    {
	        var l:Array = __builtin__.range(lst.length);
	        //trace("l = "+l);
	        var finished:Boolean = true;
	        
	        var t:PyTuple = new PyTuple();
	        
	        for each(var elem:int in l)
	        {
	            //if isinstance(this.unjellyInto(l, elem, lst[elem]), NotKnown)
	            
	            if(this.unjellyInto(t, elem, lst[elem]) == null)
	            {
	                finished = false;
	            }
	            //trace("elem = "+elem);
	        }
	        if (finished)
	        {
	            //return tuple(l);
	        }
	        else
	        {
	            //return _Tuple(l);
	        }
	        return t;
	    }
	
	    private function _unjelly_list(lst:Array):Array
	    {
	        var l:Array = __builtin__.range(lst.length);
	        for each(var elem:int in l)
	        {
	            this.unjellyInto(l, elem, lst[elem]);
	        }
	        return l;
	    }
	
	    private function _unjelly_dictionary(lst:Array):Object
	    {
	        var d:Object = {};
	        for (var k:String in lst)
	        {
	            //kvd = _DictKeyAndValue(d)
	            //this.unjellyInto(kvd, 0, k)
	            //this.unjellyInto(kvd, 1, v)
	            d[k] = lst[k];
	        }
	        return d;
	    }
	    
	    
	    
	    private function unjellyInto(obj:Object, loc:uint, jel:*):Object
	    {
	        var o:* = this.unjelly(jel)
	        if (o is NotKnown)
	        {
	            o.addDependant(obj, loc);
	        }
	        obj[loc] = o;
	        return o;
	    }
	    
		/*
		private function _unjelly_reference(lst)
	    {
	        var refid = lst[0]
	        var exp = lst[1]
	        var o = this.unjelly(exp)
	        var ref = this.references.get(refid)
	        if (ref is None):
	            this.references[refid] = o
	        elif isinstance(ref, NotKnown):
	            ref.resolveDependants(o)
	            this.references[refid] = o
	        else:
	            assert 0, "Multiple references with same ID!"
	        return o
	    }
		
		
	    private function _unjelly_None(exp:String)
	    {
	        return None
	    }
		
		*/
	    private function _unjelly_unicode(exp:Array):String
	    {
	        /*
	        if UnicodeType:
	            return unicode(exp[0], "UTF-8")
	        else:
	            return Unpersistable(exp[0])
	            */
	            return exp[0];
	    }
		/*
	    private function _unjelly_boolean(exp:String)
	    {
	        if BooleanType:
	            assert exp[0] in ('true', 'false')
	            return exp[0] == 'true'
	        else:
	            return Unpersistable(exp[0])
	    }
	
	    private function _unjelly_datetime(exp:String)
	    {
	        //return datetime.datetime(*map(int, exp[0].split()))
	    }
	
	    private function _unjelly_date(exp:String)
	    {
	        //return datetime.date(*map(int, exp[0].split()))
	    }
	
	    private function _unjelly_time(exp:String)
	    {
	        //return datetime.time(*map(int, exp[0].split()))
	    }
	
	    private function _unjelly_timedelta(exp:String)
	    {
	        days, seconds, microseconds = map(int, exp[0].split())
	        return datetime.timedelta(days=days, seconds=seconds, microseconds=microseconds)
	    }
	
	    private function unjellyInto(obj, loc, jel)
	    {
	        o = this.unjelly(jel)
	        if isinstance(o, NotKnown):
	            o.addDependant(obj, loc)
	        obj[loc] = o
	        return o
	    }
	
	    private function _unjelly_dereference(lst)
	    {
	        refid = lst[0]
	        x = this.references.get(refid)
	        if x is not None:
	            return x
	        der = _Dereference(refid)
	        this.references[refid] = der
	        return der
	    }
	
	    private function _unjelly_reference(lst)
	    {
	        refid = lst[0]
	        exp = lst[1]
	        o = this.unjelly(exp)
	        ref = this.references.get(refid)
	        if (ref is None):
	            this.references[refid] = o
	        elif isinstance(ref, NotKnown):
	            ref.resolveDependants(o)
	            this.references[refid] = o
	        else:
	            assert 0, "Multiple references with same ID!"
	        return o
	    }
	
	    private function _unjelly_tuple(lst)
	    {
	        l = range(len(lst))
	        finished = 1
	        for elem in l:
	            if isinstance(this.unjellyInto(l, elem, lst[elem]), NotKnown):
	                finished = 0
	        if finished:
	            return tuple(l)
	        else:
	            return _Tuple(l)
	    }
	
	    private function _unjelly_list(lst)
	    {
	        l = range(len(lst))
	        for elem in l:
	            this.unjellyInto(l, elem, lst[elem])
	        return l
	    }
	
	    private function _unjelly_dictionary(lst):
	    {
	        d = {}
	        for k, v in lst:
	            kvd = _DictKeyAndValue(d)
	            this.unjellyInto(kvd, 0, k)
	            this.unjellyInto(kvd, 1, v)
	        return d
	    }
	
	
	    private function _unjelly_module(rest):
	    {
	        moduleName = rest[0]
	        if type(moduleName) != types.StringType:
	            raise InsecureJelly("Attempted to unjelly a module with a non-string name.")
	        if not this.taster.isModuleAllowed(moduleName):
	            raise InsecureJelly("Attempted to unjelly module named %s" % repr(moduleName))
	        mod = __import__(moduleName, {}, {},"x")
	        return mod
	    }
	
	    private function _unjelly_class(rest):
	    {
	        clist = string.split(rest[0], '.')
	        modName = string.join(clist[:-1], '.')
	        if not this.taster.isModuleAllowed(modName):
	            raise InsecureJelly("module %s not allowed" % modName)
	        klaus = namedObject(rest[0])
	        if type(klaus) is not types.ClassType:
	            raise InsecureJelly("class %s unjellied to something that isn't a class: %s" % (repr(rest[0]), repr(klaus)))
	        if not this.taster.isClassAllowed(klaus):
	            raise InsecureJelly("class not allowed: %s" % qual(klaus))
	        return klaus
	    }
	
	    private function _unjelly_function(rest):
	    {
	        modSplit = string.split(rest[0], '.')
	        modName = string.join(modSplit[:-1], '.')
	        if not this.taster.isModuleAllowed(modName):
	            raise InsecureJelly("Module not allowed: %s"% modName)
	        # XXX do I need an isFunctionAllowed?
	        function = namedObject(rest[0])
	        return function
	    }
	
	    private function _unjelly_persistent(rest):
	    {
	        if this.persistentLoad:
	            pload = this.persistentLoad(rest[0], this)
	            return pload
	        else:
	            return Unpersistable("persistent callback not found")
	    }
	
	    private function _unjelly_instance(rest):
	    {
	        clz = this.unjelly(rest[0])
	        if type(clz) is not types.ClassType:
	            raise InsecureJelly("Instance found with non-class class.")
	        if hasattr(clz, "__setstate__"):
	            inst = _newInstance(clz, {})
	            state = this.unjelly(rest[1])
	            inst.__setstate__(state)
	        else:
	            state = this.unjelly(rest[1])
	            inst = _newInstance(clz, state)
	        if hasattr(clz, 'postUnjelly'):
	            this.postCallbacks.append(inst.postUnjelly)
	        return inst
	    }
	
	    private function _unjelly_unpersistable(rest):
	    {
	        return Unpersistable(rest[0])
	    }
	
	    private function _unjelly_method(rest):
	    {
	        ''' (internal) unjelly a method
	        '''
	        im_name = rest[0]
	        im_self = this.unjelly(rest[1])
	        im_class = this.unjelly(rest[2])
	        if type(im_class) is not types.ClassType:
	            raise InsecureJelly("Method found with non-class class.")
	        if im_class.__dict__.has_key(im_name):
	            if im_self is None:
	                im = getattr(im_class, im_name)
	            elif isinstance(im_self, NotKnown):
	                im = _InstanceMethod(im_name, im_self, im_class)
	            else:
	                im = instancemethod(im_class.__dict__[im_name],
	                                    im_self,
	                                    im_class)
	        else:
	            raise 'instance method changed'
	        return im
	    }
	    */
	}
}